 /*
  * $Header: /cvshome/build/org.osgi.util.measurement/src/org/osgi/util/measurement/Measurement.java,v 1.14 2006/07/11 00:54:06 hargrave Exp $
  *
  * Copyright (c) OSGi Alliance (2002, 2006). All Rights Reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 package org.osgi.util.measurement;


 /**
  * Represents a value with an error, a unit and a time-stamp.
  *
  * <p>
  * A <code>Measurement</code> object is used for maintaining the tuple of value,
  * error, unit and time-stamp. The value and error are represented as doubles
  * and the time is measured in milliseconds since midnight, January 1, 1970 UTC.
  *
  * <p>
  * Mathematic methods are provided that correctly calculate taking the error
  * into account. A runtime error will occur when two measurements are used in an
  * incompatible way. E.g., when a speed (m/s) is added to a distance (m). The
  * measurement class will correctly track changes in unit during multiplication
  * and division, always coercing the result to the most simple form. See
  * {@link Unit} for more information on the supported units.
  *
  * <p>
  * Errors in the measurement class are absolute errors. Measurement errors
  * should use the P95 rule. Actual values must fall in the range value +/- error
  * 95% or more of the time.
  *
  * <p>
  * A <code>Measurement</code> object is immutable in order to be easily shared.
  *
  * <p>
  * Note: This class has a natural ordering that is inconsistent with equals. See
  * {@link #compareTo}.
  *
  * @version $Revision: 1.14 $
  */
 public class Measurement implements Comparable {
     /* package private so it can be accessed by Unit */
     final double value;
     final double error;
     final long time;
     final Unit unit;
     private transient String name;

     /**
      * Create a new <code>Measurement</code> object.
      *
      * @param value The value of the <code>Measurement</code>.
      * @param error The error of the <code>Measurement</code>.
      * @param unit The <code>Unit</code> object in which the value is measured. If
      * this argument is <code>null</code>, then the unit will be set to
      * {@link Unit#unity}.
      * @param time The time measured in milliseconds since midnight, January 1,
      * 1970 UTC.
      */
     public Measurement(double value, double error, Unit unit, long time) {
         this.value = value;
         this.error = Math.abs(error);
         this.unit = (unit != null) ? unit : Unit.unity;
         this.time = time;
     }

     /**
      * Create a new <code>Measurement</code> object with a time of zero.
      *
      * @param value The value of the <code>Measurement</code>.
      * @param error The error of the <code>Measurement</code>.
      * @param unit The <code>Unit</code> object in which the value is measured. If
      * this argument is <code>null</code>, then the unit will be set to
      * {@link Unit#unity}.
      */
     public Measurement(double value, double error, Unit unit) {
         this(value, error, unit, 0l);
     }

     /**
      * Create a new <code>Measurement</code> object with an error of 0.0 and a
      * time of zero.
      *
      * @param value The value of the <code>Measurement</code>.
      * @param unit The <code>Unit</code> in which the value is measured. If this
      * argument is <code>null</code>, then the unit will be set to
      * {@link Unit#unity}.
      */
     public Measurement(double value, Unit unit) {
         this(value, 0.0d, unit, 0l);
     }

     /**
      * Create a new <code>Measurement</code> object with an error of 0.0, a unit
      * of {@link Unit#unity} and a time of zero.
      *
      * @param value The value of the <code>Measurement</code>.
      */
     public Measurement(double value) {
         this(value, 0.0d, null, 0l);
     }

     /**
      * Returns the value of this <code>Measurement</code> object.
      *
      * @return The value of this <code>Measurement</code> object as a double.
      */
     public final double getValue() {
         return value;
     }

     /**
      * Returns the error of this <code>Measurement</code> object. The error is
      * always a positive value.
      *
      * @return The error of this <code>Measurement</code> as a double.
      */
     public final double getError() {
         return error;
     }

     /**
      * Returns the <code>Unit</code> object of this <code>Measurement</code> object.
      *
      * @return The <code>Unit</code> object of this <code>Measurement</code> object.
      *
      * @see Unit
      */
     public final Unit getUnit() {
         return unit;
     }

     /**
      * Returns the time at which this <code>Measurement</code> object was taken.
      * The time is measured in milliseconds since midnight, January 1, 1970 UTC,
      * or zero when not defined.
      *
      * @return The time at which this <code>Measurement</code> object was taken or
      * zero.
      */
     public final long getTime() {
         return time;
     }

     /**
      * Returns a new <code>Measurement</code> object that is the product of this
      * object multiplied by the specified object.
      *
      * @param m The <code>Measurement</code> object that will be multiplied with
      * this object.
      * @return A new <code>Measurement</code> that is the product of this object
      * multiplied by the specified object. The error and unit of the new
      * object are computed. The time of the new object is set to the
      * time of this object.
      * @throws ArithmeticException If the <code>Unit</code> objects of this object
      * and the specified object cannot be multiplied.
      * @see Unit
      */
     public Measurement mul(Measurement m) {
         double mvalue = m.value;
         return new Measurement(value * mvalue, Math.abs(value) * m.error
                 + error * Math.abs(mvalue), unit.mul(m.unit), time);
     }

     /**
      * Returns a new <code>Measurement</code> object that is the product of this
      * object multiplied by the specified value.
      *
      * @param d The value that will be multiplied with this object.
      * @param u The <code>Unit</code> of the specified value.
      * @return A new <code>Measurement</code> object that is the product of this
      * object multiplied by the specified value. The error and unit of
      * the new object are computed. The time of the new object is set to
      * the time of this object.
      * @throws ArithmeticException If the units of this object and the specified
      * value cannot be multiplied.
      * @see Unit
      */
     public Measurement mul(double d, Unit u) {
         return new Measurement(value * d, error * Math.abs(d), unit.mul(u),
                 time);
     }

     /**
      * Returns a new <code>Measurement</code> object that is the product of this
      * object multiplied by the specified value.
      *
      * @param d The value that will be multiplied with this object.
      * @return A new <code>Measurement</code> object that is the product of this
      * object multiplied by the specified value. The error of the new
      * object is computed. The unit and time of the new object is set to
      * the unit and time of this object.
      */
     public Measurement mul(double d) {
         return new Measurement(value * d, error * Math.abs(d), unit, time);
     }

     /**
      * Returns a new <code>Measurement</code> object that is the quotient of this
      * object divided by the specified object.
      *
      * @param m The <code>Measurement</code> object that will be the divisor of
      * this object.
      * @return A new <code>Measurement</code> object that is the quotient of this
      * object divided by the specified object. The error and unit of the
      * new object are computed. The time of the new object is set to the
      * time of this object.
      * @throws ArithmeticException If the <code>Unit</code> objects of this object
      * and the specified object cannot be divided.
      * @see Unit
      */
     public Measurement div(Measurement m) {
         double mvalue = m.value;
         return new Measurement(value / mvalue,
                 (Math.abs(value) * m.error + error * Math.abs(mvalue))
                         / (mvalue * mvalue), unit.div(m.unit), time);
     }

     /**
      * Returns a new <code>Measurement</code> object that is the quotient of this
      * object divided by the specified value.
      *
      * @param d The value that will be the divisor of this object.
      * @param u The <code>Unit</code> object of the specified value.
      * @return A new <code>Measurement</code> that is the quotient of this object
      * divided by the specified value. The error and unit of the new
      * object are computed. The time of the new object is set to the
      * time of this object.
      * @throws ArithmeticException If the <code>Unit</code> objects of this object
      * and the specified object cannot be divided.
      * @see Unit
      */
     public Measurement div(double d, Unit u) {
         return new Measurement(value / d, error / Math.abs(d), unit.div(u),
                 time);
     }

     /**
      * Returns a new <code>Measurement</code> object that is the quotient of this
      * object divided by the specified value.
      *
      * @param d The value that will be the divisor of this object.
      * @return A new <code>Measurement</code> object that is the quotient of this
      * object divided by the specified value. The error of the new
      * object is computed. The unit and time of the new object is set to
      * the <code>Unit</code> and time of this object.
      */
     public Measurement div(double d) {
         return new Measurement(value / d, error / Math.abs(d), unit, time);
     }

     /**
      * Returns a new <code>Measurement</code> object that is the sum of this
      * object added to the specified object.
      *
      * The error and unit of the new object are computed. The time of the new
      * object is set to the time of this object.
      *
      * @param m The <code>Measurement</code> object that will be added with this
      * object.
      * @return A new <code>Measurement</code> object that is the sum of this and
      * m.
      * @see Unit
      * @throws ArithmeticException If the <code>Unit</code> objects of this object
      * and the specified object cannot be added.
      */
     public Measurement add(Measurement m) {
         return new Measurement(value + m.value, error + m.error, unit
                 .add(m.unit), time);
     }

     /**
      * Returns a new <code>Measurement</code> object that is the sum of this
      * object added to the specified value.
      *
      * @param d The value that will be added with this object.
      * @param u The <code>Unit</code> object of the specified value.
      * @return A new <code>Measurement</code> object that is the sum of this
      * object added to the specified value. The unit of the new object
      * is computed. The error and time of the new object is set to the
      * error and time of this object.
      * @throws ArithmeticException If the <code>Unit</code> objects of this object
      * and the specified value cannot be added.
      * @see Unit
      */
     public Measurement add(double d, Unit u) {
         return new Measurement(value + d, error, unit.add(u), time);
     }

     /**
      * Returns a new <code>Measurement</code> object that is the sum of this
      * object added to the specified value.
      *
      * @param d The value that will be added with this object.
      * @return A new <code>Measurement</code> object that is the sum of this
      * object added to the specified value. The error, unit, and time of
      * the new object is set to the error, <code>Unit</code> and time of
      * this object.
      */
     public Measurement add(double d) {
         return new Measurement(value + d, error, unit, time);
     }

     /**
      * Returns a new <code>Measurement</code> object that is the subtraction of
      * the specified object from this object.
      *
      * @param m The <code>Measurement</code> object that will be subtracted from
      * this object.
      * @return A new <code>Measurement</code> object that is the subtraction of
      * the specified object from this object. The error and unit of the
      * new object are computed. The time of the new object is set to the
      * time of this object.
      * @throws ArithmeticException If the <code>Unit</code> objects of this object
      * and the specified object cannot be subtracted.
      * @see Unit
      */
     public Measurement sub(Measurement m) {
         return new Measurement(value - m.value, error + m.error, unit
                 .sub(m.unit), time);
     }

     /**
      * Returns a new <code>Measurement</code> object that is the subtraction of
      * the specified value from this object.
      *
      * @param d The value that will be subtracted from this object.
      * @param u The <code>Unit</code> object of the specified value.
      * @return A new <code>Measurement</code> object that is the subtraction of
      * the specified value from this object. The unit of the new object
      * is computed. The error and time of the new object is set to the
      * error and time of this object.
      * @throws ArithmeticException If the <code>Unit</code> objects of this object
      * and the specified object cannot be subtracted.
      * @see Unit
      */
     public Measurement sub(double d, Unit u) {
         return new Measurement(value - d, error, unit.sub(u), time);
     }

     /**
      * Returns a new <code>Measurement</code> object that is the subtraction of
      * the specified value from this object.
      *
      * @param d The value that will be subtracted from this object.
      * @return A new <code>Measurement</code> object that is the subtraction of
      * the specified value from this object. The error, unit and time of
      * the new object is set to the error, <code>Unit</code> object and
      * time of this object.
      */
     public Measurement sub(double d) {
         return new Measurement(value - d, error, unit, time);
     }

     /**
      * Returns a <code>String</code> object representing this <code>Measurement</code>
      * object.
      *
      * @return a <code>String</code> object representing this <code>Measurement</code>
      * object.
      */
     public String toString() {
         if (name == null) {
             StringBuffer sb = new StringBuffer ();
             sb.append(value);
             if (error != 0.0d) {
                 sb.append(" +/- ");
                 sb.append(error);
             }
             String u = unit.toString();
             if (u.length() > 0) {
                 sb.append(" ");
                 sb.append(u);
             }
             name = sb.toString();
         }
         return name;
     }

     /**
      * Compares this object with the specified object for order. Returns a
      * negative integer, zero, or a positive integer if this object is less
      * than, equal to, or greater than the specified object.
      *
      * <p>
      * Note: This class has a natural ordering that is inconsistent with equals.
      * For this method, another <code>Measurement</code> object is considered
      * equal if there is some <code>x</code> such that
      *
      * <pre>
      * getValue() - getError() &lt;= x &lt;= getValue() + getError()
      * </pre>
      *
      * for both <code>Measurement</code> objects being compared.
      *
      * @param obj The object to be compared.
      * @return A negative integer, zero, or a positive integer if this object is
      * less than, equal to, or greater than the specified object.
      *
      * @throws ClassCastException If the specified object is not of type
      * <code>Measurement</code>.
      * @throws ArithmeticException If the unit of the specified
      * <code>Measurement</code> object is not equal to the <code>Unit</code>
      * object of this object.
      */
     public int compareTo(Object obj) {
         if (this == obj) {
             return 0;
         }
         Measurement that = (Measurement) obj;
         if (!unit.equals(that.unit)) {
             throw new ArithmeticException ("Cannot compare " + this + " and "
                     + that);
         }
         if (value == that.value) {
             return 0;
         }
         if (value < that.value) {
             if ((value + error) >= (that.value - that.error)) {
                 return 0;
             }
             else {
                 return -1;
             }
         }
         else {
             if ((value - error) <= (that.value + that.error)) {
                 return 0;
             }
             else {
                 return 1;
             }
         }
     }

     /**
      * Returns a hash code value for this object.
      *
      * @return A hash code value for this object.
      */
     public int hashCode() {
         long bits = Double.doubleToLongBits(value + error);
         return ((int) (bits ^ (bits >>> 32))) ^ unit.hashCode();
     }

     /**
      * Returns whether the specified object is equal to this object. Two
      * <code>Measurement</code> objects are equal if they have same value, error
      * and <code>Unit</code>.
      *
      * <p>
      * Note: This class has a natural ordering that is inconsistent with equals.
      * See {@link #compareTo}.
      *
      * @param obj The object to compare with this object.
      * @return <code>true</code> if this object is equal to the specified object;
      * <code>false</code> otherwise.
      */
     public boolean equals(Object obj) {
         if (this == obj) {
             return true;
         }
         if (!(obj instanceof Measurement)) {
             return false;
         }
         Measurement that = (Measurement) obj;
         return (value == that.value) && (error == that.error)
                 && unit.equals(that.unit);
     }
 }

